package com.aube.control.player;

import android.content.Context;
import android.graphics.SurfaceTexture;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Handler;
import android.os.Message;
import android.view.Surface;
import android.view.TextureView;

import com.huyn.baseframework.model.VideoPlayModel;
import com.huyn.baseframework.utils.Utils;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Created by huyn on 2015/9/23.
 * 普通镜头的视频处理逻辑
 */
public class TextureVideoHelper extends AbstractVideoHelper {

    private Timer mTimer = null;

    public TextureVideoHelper(){}

    public TextureVideoHelper(TextureView textureView, Context context, VideoPlayModel video) {
        this(textureView, context, video, false);
    }

    public TextureVideoHelper(TextureView textureView, Context context, VideoPlayModel video, boolean enableVolume) {
        this.enableVolume = enableVolume;
        this.mTexture = textureView;
        this.mContext = context;
        initParamOfFirstLanch();
        initPlayInfo(context, video);
    }

    private void initParamOfFirstLanch() {
        //各种监听
        myListener = createMyListener();

        mTexture.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                Utils.Log(TAG, "surface create!!!!!!!!!!");
                texturePrepared(new Surface(surface));
            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
//                player.setSurface(new Surface(surface));
                Utils.Log(TAG, "major surface destroyed!!!!!!!!!!");
            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                Utils.Log(TAG, "surface destroyed!!!!!!!!!!");
//                onDestroy();
                try {
                    if (!isDestroyed.get()) {
                        mRunningOnBackground.set(true);
                        if (player != null)
                            player.pause();
                        else if (exoPlayer != null) {
                            exoPlayer.setPlayWhenReady(false);
//                            exoPlayer.blockingClearSurface();
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return true;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {
//                Utils.Log(TAG, "major surface updated!!!!!!!!!!");
            }
        });
    }

    protected MyMediaPlayerListener createMyListener() {
        return new MyMediaPlayerListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                if(!prepared.get())
                    return;
                Utils.Log(getVideoId(), "play complete...") ;
                if (iPlayCallback != null) {
                    iPlayCallback.endPlay();
                }
            }

            @Override
            public void onPrepared(MediaPlayer mp) {
                prepared.set(true);

                Utils.Log(getVideoId(), "prepare to play : " + player.getDuration()) ;
                play();
            }

            @Override
            public void onBufferingUpdate(MediaPlayer mp, int percent) {
                mBufferdPercent = percent;
            }

            @Override
            public void onSeekComplete(final MediaPlayer mp) {
                onMediaPlayerSeekComplete(mp);
            }
        };
    }

    protected void texturePrepared(Surface surface) {
        if(surface == null)
            return;
        try {
            Utils.Log(getVideoId(), "prepare surface");
            if (mRunningOnBackground.get()) {
                mRunningOnBackground.set(false);
                //按home键暂停,会黑屏
                //player.prepareAsync();
                //player.start();
            }

            isDestroyed.set(false);

//            manageSound(enableVolume);

            if(player != null) {
                player.setSurface(surface);

                player.setAudioStreamType(AudioManager.STREAM_MUSIC);
                player.setOnBufferingUpdateListener(myListener);
                player.setOnPreparedListener(myListener);
                player.setOnCompletionListener(myListener);
                player.setOnSeekCompleteListener(myListener);
                player.setOnErrorListener(myListener);
            } else {
                textureForExoPrepared(surface);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void onDestroy() {
        try {
            Utils.Log(getVideoId(), "player destroy ..........");
            super.onDestroy();

            isDestroyed.set(true);

            if(mTimer != null)
                mTimer.cancel();
            mTimer = null;

            mHandler.removeCallbacksAndMessages(null);

            stopPlayer();
            releasePlayer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if(msg.what==PLAY_WATCH) {
                if(player != null && player.isPlaying()) {
                    int currentPos = player.getCurrentPosition();
                    int duration = player.getDuration();
                    if(iPlayCallback != null) {
                        iPlayCallback.updatePlay(currentPos, duration);

//                        if(!iPlayCallback.hasFocused())
//                            pausePlay();
                    }
                }

                if(exoPlayer != null && exoPlayer.getPlayWhenReady()) {
                    int currentPos = (int) exoPlayer.getCurrentPosition();
                    int duration = (int) exoPlayer.getDuration();
                    mBufferdPercent = exoPlayer.getBufferedPercentage();
                    if(iPlayCallback != null) {
                        iPlayCallback.updatePlay(currentPos, duration);
                    }
                }
            }
        }
    };

    public boolean play() {
        if(prepared.get()) {
            if(mMainVideoPaused.get())
                return false;

            if(player != null)
                player.start();

            if (iPlayCallback != null)
                iPlayCallback.startPlay();

            startTimer();
            return true;
        }
        return false;
    }

    public boolean startPlay() {
        mMainVideoPaused.set(false);
        Utils.Log(getVideoId(), "start play ..........");
        boolean startFromPause = false;
        if (player != null && !player.isPlaying()) {
            player.start();
            startFromPause = true;
        } else if(exoPlayer != null && !exoPlayer.getPlayWhenReady()) {
            exoPlayer.setPlayWhenReady(true);
            startFromPause = true;
        }

        mHandler.sendEmptyMessage(PLAY_WATCH);
        startTimer();
        return startFromPause;
    }

    public void startPlay(long progress) {
        mMainVideoPaused.set(false);
        if(isDestroyed.get()) {
            isDestroyed.set(false);

            if (player != null) {
                player.prepareAsync();
                player.start();
            } else if(exoPlayer != null) {
                exoPlayer.setPlayWhenReady(true);
            }

            mHandler.sendEmptyMessage(PLAY_WATCH);

            startTimer();
        }
//        long target = progress*1000-startTime;
//        if(target > getDuration())
//            return;
//        player.seekTo((int) (progress*1000 - startTime));
//        Utils.Log(getVideoId(), "start play again .........." + (progress*1000-startTime) + "/" + progress + "/" + player.getDuration());
    }

    public void pausePlay() {
        mMainVideoPaused.set(true);
//        Utils.Log(getVideoId(), "toggle...true");

        if(iPlayCallback != null)
            iPlayCallback.clearLoading();

        pausePlayer();

        if(mTimer != null) {
            mTimer.cancel();
            mTimer = null;
        }
    }

    @Override
    public void seekTo(int pos) {
        if(startTime < 0)
            return;
        if(isDestroyed.get())
            return;
        if(!seekComplete.get())
            return;
        if(!isPrepared())
            return;
        if(player == null && exoPlayer == null)
            return;
        int target = (int) (pos - startTime);
        Utils.Log(getVideoId(), "doseeking>>>>>>>>>" + target);
        asyncSeek(target, false);
    }

    private void startTimer() {
        // 通过定时器和Handler来更新进度
        if(mTimer != null)
            return;

        Utils.Log(getVideoId(), "******************start to starttimer******************");
        mTimer = new Timer();
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                if (!isPrepared()) {
                    return;
                }
                mHandler.sendEmptyMessage(PLAY_WATCH);
            }
        };
        mTimer.schedule(timerTask, 0, 100);
        scheduleVideoAsync();
    }

    @Override
    public int getCachedBuffer() {
        try {
            if(player != null) {
                if (mBufferdPercent == 100 && mCachedData < 10)
                    return ILLEGAL_BUFFER_RESULT;

                int duration = player.getDuration();
//                Utils.Log("BALANCE", getVideoId() + ":" + player.getCurrentPosition() + "/" + player.getDuration() + "/" + mBufferdPercent + "/" + mCachedData + "/" + mDownloadedData);
//            if(mBufferdPercent == 100 && player.getCurrentPosition() < duration)
                if (mBufferdPercent == 100 && player.getCurrentPosition() == 0)
                    return ILLEGAL_BUFFER_RESULT;

                long currentProgress = current - startTime;
                int bufferProgress = (int) (mBufferdPercent * duration / 100);
                int result = (int) ((bufferProgress - currentProgress) / 1000);

                if (mBufferdPercent == 100 && result <= 0)
                    return PLAY_END;

                return result;
            } else {
                int mBufferdPercent = exoPlayer.getBufferedPercentage();
                if (mBufferdPercent == 100) {
                    if (mCachedData < 10)
                        return ILLEGAL_BUFFER_RESULT;
                    else
                        return BUFFER_COMPLETE;
                }

//                long duration = getDuration();
//                Utils.Log("BALANCE", getVideoId() + ":" + getCurrentProgress() + "/" + duration + "/" + mBufferdPercent + "/" + mCachedData + "/" + mDownloadedData);
                if (mBufferdPercent == 100 && getCurrentProgress() == 0)
                    return ILLEGAL_BUFFER_RESULT;

                long currentProgress = current - startTime;
                int result = (int) ((exoPlayer.getBufferPosition() - currentProgress) / 1000);

                if (mBufferdPercent == 100 && result <= 0)
                    return PLAY_END;

                return result;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ILLEGAL_BUFFER_RESULT;
    }

    /**
     * =============================================================================================
     * 分镜头同步模块
     */
    private AtomicBoolean mFromSyncSeek = new AtomicBoolean(false);
    private AtomicBoolean mMainVideoPaused = new AtomicBoolean(false);
    protected static final int SYNC_DURATION = 1000;
    protected void scheduleVideoAsync() {
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        syncPlay();
                    }
                });
            }
        };
        mTimer.schedule(task, SYNC_DURATION, SYNC_DURATION);
    }

    public synchronized void syncPlay() {
//        if(isPrepared() && isPlaying()) {
        if(mRunningOnBackground.get())
            return;
        try {
            if (isPrepared()) {
                doSync();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void updateProgress(long current) {
        this.current = current;
    }

    public void setStartTime(long time) {
        this.startTime = time*1000;
    }

    public long getStartTime() {
        return startTime;
    }

    public void initPlayState(boolean play) {
        mMainVideoPaused.set(!play);
//        Utils.Log(getVideoId(), "set toggle..." + (play ? "false" : "true"));
    }

    private long startTime = -1;
    private long current = 0;
    private AtomicBoolean seekComplete = new AtomicBoolean(true);
    private int PAUSE_DELAY_DURATION = 200;
    private long lastPlayPos = 0;
    private int tartSeekPos = 0;
    private AtomicBoolean mIsPaused = new AtomicBoolean(false);
    private static final int SECTION_FOR_SYNC = 200;
    private int waitSecond = 0;
    private synchronized void doSync() {
//        Utils.Log(getVideoId(), "start to async..." + (mMainVideoPaused.get() ? "true" : "false"));
        if(isDestroyed.get())
            return;

        if(MultiVideoBalanceAdapter.getInstance().isLowExperience())
            return;

        //为了防止暂停的时候还在同步
        if(mMainVideoPaused.get()) {
            pausePlay();
            return;
        }

        long currentProgress = getCurrentProgress();

        if(startTime < 0) {
            if(currentProgress > 1000 && !mIsPaused.get()) {
                Utils.Log(getVideoId(), "buffering:" + mBufferdPercent + "---now:" + currentProgress);
                mIsPaused.set(true);
                pausePlayer();
                Utils.Log(getVideoId(), "pause");
            }
            return;
        }

        if(!seekComplete.get())
            return;

        if(mIsPaused.get()) {
            Utils.Log(getVideoId(), "awaking after:" + (currentProgress - current + startTime));
            awake(currentProgress, current-startTime);
            return;
        }

        long progress = current - startTime;
        long duration = getDuration();

        if(duration <= 0)
            return;

        int actualPercent = (int) (progress*100/duration);

        Utils.Log(getVideoId(), "cur:" + currentProgress + "__to:" + progress + "_ lastpos:" + tartSeekPos + "___" + mBufferdPercent+"/"+actualPercent + "_duration:" + duration + "_starttime:" + startTime);

        if(progress >= duration) {
            //TODO 拖动
//            isDestroyed.set(true);

            //end play
//            if(iPlayCallback != null)
//                iPlayCallback.endPlay();

//            stopPlayer();
            return;
        }

        //在允许的误差范围内默认同步成功
        if(Math.abs(currentProgress-progress) < SECTION_FOR_SYNC) {
            //同步成功
            if(iPlayCallback != null)
                iPlayCallback.syncSuccess();
            return;
        }

        if(currentProgress > progress) {
            //往回拉太多需要跳到拖动后5s的地方播放
            if(currentProgress >= progress + 5000) {
//                progress += 5000;
                lastPlayPos = currentProgress;

                doSeek((int) progress);
                return;
            } else {
                //做一个延迟
                Utils.Log(getVideoId(), "make a waiting..........." + (currentProgress - progress));
                makeAWait(currentProgress, progress);
                return;
            }
        } else {
//            if(bufferPercent > actualPercent) {
//                Utils.Log(getVideoId(), "................seek to:" + (progress+1000));
//                seekComplete.set(false);
//                player.seekTo((int) progress+1000);
//                seekDelta = 0;
//                lastSeekTime = System.currentTimeMillis();
//                return;
//            }
        }

        //如果处于低速模式，就再往后5s开始缓冲，避免频繁seek带来的性能问题
        //如果已经seek过，下一次的目标位置在上一次之前，就跳过好啦
        if(seekDelta < 0)
            seekDelta = 100;
        if(tartSeekPos >= progress + 1000 + seekDelta)
            return;
//        if(Math.abs(currentProgress-lastPlayPos) < 2000)
//            progress += 5000;
        int bufferProgress = (int) (mBufferdPercent * duration);
        Utils.Log(getVideoId(), "buffering progress..........." + bufferProgress + "...waitsecond:" + waitSecond);
        if(progress <= currentProgress + 10000) {
            //可能正在缓冲,稍微等等
            if(bufferProgress >= progress + 2000) {
                lastPlayPos = currentProgress;
                doSeek(progress + 500);
            } else {
                if(waitSecond >= 5) {
                    waitSecond = 0;
                    lastPlayPos = currentProgress;
                    doSeek(progress+5000);
                } else {
                    waitSecond++;
                }
            }
            return;
        } else {
            if(bufferProgress >= progress + 2000) {
                progress += 500;
            } else {
                progress += 5000;
            }
        }
        lastPlayPos = currentProgress;

        doSeek((int) progress);
    }


    private long seekDelta = 100;//default value
    private long lastSeekTime = 0;
    public void doSeek(long progress) {
//        if(seekDelta == 0)
//            lastSeekTime = System.currentTimeMillis();

//        if(!player.isPlaying())
//            return;
        Utils.Log(getVideoId(), "-----------------prepare to seek");
        if(mMainVideoPaused.get() || !isPrepared())
            return;

        //给一个限定...不能太大,不然视频跳太多了
        if(seekDelta >= 5000)
            seekDelta = 5000;

        tartSeekPos = (int) (progress + seekDelta);
        Utils.Log(getVideoId(), "-----------------seek to:" + tartSeekPos);

        asyncSeek(tartSeekPos, true);

        seekDelta = 0;
        lastSeekTime = current;
    }

    private synchronized void asyncSeek(int target, boolean blockSeek) {
        seekComplete.set(false);
        mFromSyncSeek.set(blockSeek);
        Utils.Log(getVideoId(), "=====================>execute seeking:" + target);
        seekPlayer(target);
        if(getDuration() > 0) {
            mBufferdPercent = target * 100f / getDuration();
        } else {
            mBufferdPercent = 0;
        }
    }

    private void awake(long current, long target) {
        seekComplete.set(false);
        final long pause = current - target;
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if(mMainVideoPaused.get())
                    return;
                startPlayer();
                mIsPaused.set(false);
                seekComplete.set(true);
            }
        }, pause);
    }

    private void makeAWait(long current, long target) {
        seekComplete.set(false);
        pausePlayer();
        final long pause = current - target;
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if(mMainVideoPaused.get())
                    return;
                startPlay();
                seekComplete.set(true);
            }
        }, pause);
    }

    private void onMediaPlayerSeekComplete(final MediaPlayer mp) {
        if(!mFromSyncSeek.get()) {
            seekComplete.set(true);
            return;
        }
        if(startTime < 0 || current == 0)
            return;
        final long current = TextureVideoHelper.this.current;
        if(seekDelta == 0)
            seekDelta = current - lastSeekTime;

        final long offset = mp.getCurrentPosition() - (current-startTime);
        Utils.Log(getVideoId(), "seek:" + seekDelta);
        Utils.Log(getVideoId(), "now:" + mp.getCurrentPosition() + "--" + (current-startTime) + "--offset:" + offset);
        if(offset > SECTION_FOR_SYNC) {
            mp.pause();
            final long pause = offset > PAUSE_DELAY_DURATION ? (offset-PAUSE_DELAY_DURATION) : 0;
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    seekComplete.set(true);
                    if(isDestroyed.get())
                        return;
                    if(player == null)
                        return;
                    if(mMainVideoPaused.get())
                        return;

                    try {
                        Utils.Log(getVideoId(), "pause for:" + pause + " actually : " + (TextureVideoHelper.this.current - current));
                        mp.start();
                        long targetPos = (TextureVideoHelper.this.current - startTime);
                        if (mp.getCurrentPosition() - targetPos > 100)
                            PAUSE_DELAY_DURATION = 100;
                        else
                            PAUSE_DELAY_DURATION = 200;
                        Utils.Log(getVideoId(), "after pause cur:" + mp.getCurrentPosition() + "--" + targetPos);
                        Utils.Log(getVideoId(), "PAUSE_DELAY_DURATION============================" + PAUSE_DELAY_DURATION);
                    } catch (Exception e) {
                        e.printStackTrace();
                        Utils.Log(getVideoId(), "seek error!!!!!!");
                    }
                }
            }, pause);
        } else {
            seekComplete.set(true);
        }
    }

    public void onExoSeekComplete() {
        if(!mFromSyncSeek.get()) {
            seekComplete.set(true);
            return;
        }
        if(startTime < 0 || current == 0)
            return;
        final long current = TextureVideoHelper.this.current;
        if(seekDelta == 0)
            seekDelta = current - lastSeekTime;

        final long offset = getCurrentProgress() - (current-startTime);
        Utils.Log(getVideoId(), "seek:" + seekDelta);
        Utils.Log(getVideoId(), "now:" + getCurrentProgress() + "--" + (current-startTime) + "--offset:" + offset);
        if(offset > SECTION_FOR_SYNC) {
            exoPlayer.setPlayWhenReady(false);
            final long pause = offset > PAUSE_DELAY_DURATION ? (offset-PAUSE_DELAY_DURATION) : 0;
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    seekComplete.set(true);
                    if(isDestroyed.get())
                        return;
                    if(mMainVideoPaused.get())
                        return;

                    try {
                        Utils.Log(getVideoId(), "pause for:" + pause + " actually : " + (TextureVideoHelper.this.current - current));
                        exoPlayer.setPlayWhenReady(true);
                        long targetPos = (TextureVideoHelper.this.current - startTime);
                        if (getCurrentProgress() - targetPos > 100)
                            PAUSE_DELAY_DURATION = 100;
                        else
                            PAUSE_DELAY_DURATION = 200;
                        Utils.Log(getVideoId(), "after pause cur:" + getCurrentProgress() + "--" + targetPos);
                        Utils.Log(getVideoId(), "PAUSE_DELAY_DURATION============================" + PAUSE_DELAY_DURATION);
                    } catch (Exception e) {
                        e.printStackTrace();
                        Utils.Log(getVideoId(), "seek error!!!!!!");
                    }
                }
            }, pause);
        } else {
            seekComplete.set(true);
        }
    }

    /**
     * 分镜头同步模块
     * =============================================================================================
     */

}
